package com.huitone.smspfm.oldclient.core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Date;

import com.huitone.smspfm.oldclient.logger.ExtLogger;

import com.bin.common.utils.DateTimeUtils;
import com.bin.common.utils.FileUtils;
import com.bin.common.utils.StringUtils;

/**   
* @Title TestFileWriter.java 
* @Package com.huitone.smspfm.oldclient.module 
* @Description TODO(用一句话描述该文件做什么) 
* @author ybliang   
* @date 2017年5月24日 上午12:43:16 
* @version V1.0   
*/
public class TestFileWriter implements com.huitone.smspfm.oldclient.core.FileWriter {
	
	private static final ExtLogger logger = ExtLogger.create(TestFileWriter.class);
	
	/**
	 * 写文件的目录
	 */
	private String filePath = this.getClass().getResource("/").getPath();
	
	/**
	 * 当前在写的文件名
	 */
	private String fileName = "test";
	
	/**
	 * 当前在写的文件
	 */
	private String file = filePath + File.separator + fileName;
	
	/**
	 * 当文件在指定时间内没有发生修改则进行文件切割的时间间隔,单位(ms)
	 */
	private long timeToCut = 60000;
	
	/**
	 * 文件在指定时间间隔内,即便没有达到最大行值也必须必须切割
	 */
	private long intvToCut = 300000;
	
	/**
	 * 最近一次切割文件的时间
	 */
	private Date lastCutTime = new Date();
	
	/**
	 * 行数达到该值时将切割文件
	 */
	private long lineToCut = 1000;
	
	/**
	 * 当前在写的文件行数
	 */
	private long lineCount = 0;
	
	private String wrapLeft = null;
	
	private String wrapRight = null;
	
	/**
	 * 切割后的文件名
	 */
	private CutFileNameCofig cutFileName = new CutFileNameCofig();
	
	private BufferedWriter bufferedWriter = null;
	
	private BufferedReader bufferedReader = null;
	
	private FileWatchDog watchDog = null;
	
	public TestFileWriter(){}
	
	public TestFileWriter(String filePath, String fileName) {
		
		if (!StringUtils.isEmpty(filePath)) {
			this.filePath = filePath;
		}

		if (!StringUtils.isEmpty(fileName)) {
			this.fileName = fileName;
		}
		
		init();
	}
	
	public static void main(String[] args) throws IOException {
		
		long start = System.currentTimeMillis();
		
		TestFileWriter fileWriter = new TestFileWriter("/home/ybliang/test", "test");
		fileWriter.setWrapLeft("<data>");
		fileWriter.setWrapRight("</data>");
		fileWriter.setCutFileName(fileWriter.new CutFileNameCofig("resp_", ".xml"));
		
		for (int i = 0 ; i < 1999; i ++) {
			String string = "oracle自带的开发工具都是经过优化的，可以放心使用的，效率应该是最好的。oracle自带的开发工具都是经过优化的，可以放心使用的，效率应该是最好的。";
			fileWriter.write(string);
		}
		
		long end = System.currentTimeMillis();
		
		System.out.println(end - start);
	}
	
	public void init() {
		
		file = filePath + File.separator + fileName;
		
		try {
			
			bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(FileUtils.createFile(file)), "GBK"));
			
			lineCount = bufferedReader.lines().count();
			
			bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "GBK"));
			
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(getClass().getSimpleName()+"init()　 进程异常");
		}
	}
	
	public void enableWatchDog() {
		
		if (watchDog == null || !watchDog.isAlive()) {
			
			watchDog = new FileWatchDog();
			watchDog.setDs(new DoSome() {
				
				@Override
				public void doSome() {
					
					File filz = new File(file);
					long lastModified = filz.lastModified();
					long currentTimeMillis = System.currentTimeMillis();
					
					if (lastModified != 0 && lineCount != 0 && ((currentTimeMillis - lastModified >= timeToCut) || (currentTimeMillis - lastCutTime.getTime() >= intvToCut))) {
						try {
							cut();
						} catch (IOException e) {
							logger.error("文件切割的时候发生错误", e);
						}
					}
				}
			});
			watchDog.start();
		}
	}
	
	public synchronized void writeLine(String string) throws IOException {
		
		if (!StringUtils.isEmpty(string)) {
			
			if (lineCount == 0) {
				if (!StringUtils.isEmpty(wrapLeft)) {
					bufferedWriter.write(wrapLeft);
					bufferedWriter.newLine();
				}
			}
			
			bufferedWriter.write(string);
			bufferedWriter.newLine();
			bufferedWriter.flush();
			
			lineCount ++;
			
			if (lineCount >= lineToCut) {
				cut();
			}
		}
	}
	
	public synchronized void cut() throws IOException {
		
		if (!StringUtils.isEmpty(wrapRight)) {
			bufferedWriter.write(wrapRight);
			bufferedWriter.newLine();
		}
		bufferedReader.close();
		bufferedWriter.flush();
		bufferedWriter.close();
		
		File filz = new File(file);
		FileUtils.rename(filz, cutFileName.toString());
		if (!filz.exists()) {
			filz = FileUtils.createFile(file);
		}
		init();
//		bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "GBK"));
////
//		lineCount = 0;
		
		lastCutTime = new Date();
	}
	
	public String getFilePath() {
		return filePath;
	}


	public void setFilePath(String filePath) {
		this.filePath = filePath;
		this.file = filePath + File.separator + fileName;
	}


	public String getFileName() {
		return fileName;
	}


	public void setFileName(String fileName) {
		this.fileName = fileName;
		this.file = filePath + File.separator + fileName;
	}


	public String getfile() {
		return file;
	}

	
	public long getTimeToCut() {
		return timeToCut;
	}


	public void setTimeToCut(long timeToCut) {
		this.timeToCut = timeToCut;
	}


	public long getLineToCut() {
		return lineToCut;
	}


	public void setLineToCut(long lineToCut) {
		this.lineToCut = lineToCut;
	}
	
	public CutFileNameCofig getCutFileName() {
		return cutFileName;
	}

	public void setCutFileName(CutFileNameCofig cutFileName) {
		this.cutFileName = cutFileName;
	}
	
	public String getWrapLeft() {
		return wrapLeft;
	}

	public void setWrapLeft(String wrapLeft) {
		this.wrapLeft = wrapLeft;
	}

	public String getWrapRight() {
		return wrapRight;
	}

	public void setWrapRight(String wrapRight) {
		this.wrapRight = wrapRight;
	}
	
	interface DoSome {
		void doSome();
	}
	
	class FileWatchDog extends Thread {
		
		private DoSome ds = null;
		
		private boolean keepGoing = true;
		
		public DoSome getDs() {
			return ds;
		}

		public void setDs(DoSome ds) {
			this.ds = ds;
		}

		@Override
		public void run() {
			
			while (!this.isInterrupted() && isKeepGoing()) {
				
				ds.doSome();
				
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {}
			}
		}

		public boolean isKeepGoing() {
			return keepGoing;
		}

		public void setKeepGoing(boolean keepGoing) {
			this.keepGoing = keepGoing;
		}
	}

	public class CutFileNameCofig {
		
		private int serial = 0;
		private int maxSerial = 9999;
		private boolean useSerial = true;
		
		private String prefix = null;
		private String suffix = null;
		private String timePattern = "yyyyMMddHHmmssSSS";
		
		public CutFileNameCofig() {}
		
		public CutFileNameCofig(String prefix, String suffix) {
			this.prefix = prefix;
			this.suffix = suffix;
		}
		
		public String getPrefix() {
			return prefix;
		}
		public void setPrefix(String prefix) {
			this.prefix = prefix;
		}
		public String getSuffix() {
			return suffix;
		}
		public void setSuffix(String suffix) {
			this.suffix = suffix;
		}
		
		public synchronized String getSerial() {
			
			if (serial >= maxSerial) {
				serial = 0;
			} else {
				serial ++;
			}
			
			return String.format("%04d", serial);
		}
		
		public int getMaxSerial() {
			return maxSerial;
		}
		public void setMaxSerial(int maxSerial) {
			this.maxSerial = maxSerial;
		}
		public boolean isUseSerial() {
			return useSerial;
		}
		public void setUseSerial(boolean useSerial) {
			this.useSerial = useSerial;
		}
		public String getTimePattern() {
			return timePattern;
		}
		public void setTimePattern(String timePattern) {
			this.timePattern = timePattern;
		}
		@Override
		public String toString() {
			
			StringBuffer buffer = new StringBuffer();
			buffer.append(StringUtils.toString(prefix));
			buffer.append(DateTimeUtils.dateToStr(new Date(), timePattern));
			if (useSerial) {
				buffer.append(getSerial());
			}
			buffer.append(StringUtils.toString(suffix));
			
			return  buffer.toString();
		}
	}

	@Override
	public void write(Object obj) throws IOException {
		
	}

	@Override
	public void flush() throws IOException {
		
	}

	public long getIntvToCut() {
		return intvToCut;
	}

	public void setIntvToCut(long intvToCut) {
		this.intvToCut = intvToCut;
	}

	@Override
	public void destroy() {
		
		try {
			this.flush();
		} catch (IOException e) {
			logger.error("执行flush()方法出错", e);
		}
		
		if (watchDog != null) {
			watchDog.setKeepGoing(false);
		}
		
	}

}
